package cn.tedu.lock.spinlock;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * TicketLock的实现方式是，线程获取锁之后，将它的排队号返回，等该线程释放锁的时候，需要将该排队号传入。
 * 但这样是有风险的，因为这个排队号是可以被修改的，一旦排队号被不小心修改了，那么锁将不能被正确释放。一种更好的实现方式如下：
 * Date: 2016年1月4日 下午6:11:50
 *
 * @author medusar
 */
public class TicketLockV2 {

    /**
     * 服务号
     */
    private AtomicInteger serviceNum = new AtomicInteger();

    /**
     * 排队号
     */
    private AtomicInteger ticketNum = new AtomicInteger();

    /**
     * 新增一个ThreadLocal，用于存储每个线程的排队号
     */
    private ThreadLocal ticketNumHolder = new ThreadLocal();

    public void lock() {
        int currentTicketNum = ticketNum.incrementAndGet();
        // 获取锁的时候，将当前线程的排队号保存起来
        ticketNumHolder.set(currentTicketNum);
        while (currentTicketNum != serviceNum.get()) {
            // Do nothing
        }
    }

    public void unlock() {
        // 释放锁，从ThreadLocal中获取当前线程的排队号
        Integer currentTickNum = (Integer) ticketNumHolder.get();
        serviceNum.compareAndSet(currentTickNum, currentTickNum + 1);
    }
}